package com.rentcars.security;

import com.rentcars.entity.Account;
import com.rentcars.entity.Resource;
import com.rentcars.mapper.AccountMapper;
import com.rentcars.mapper.ResourceMapper;
import java.util.List;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 类描述：shiro登录认证和权限认证
 *
 * @author 张吉哲
 * @date 2021/3/2 11:20
 */
public class ShiroRealmImpl extends AuthorizingRealm {

  /**
   * 日志
   */
  private static final Logger logger = LoggerFactory.getLogger(ShiroRealmImpl.class);

  /**
   * 用户数据处理对象
   */
  @Autowired
  private AccountMapper accountMapper;

  /**
   * 权限处理对象
   */
  @Autowired
  private ResourceMapper resourceMapper;


  /**
   * 权限认证，为当前登录的Subject授予角色和权限 <br>
   *
   * @see 本例中该方法的调用时机为需授权资源被访问时 <br>
   * @see 并且每次访问需授权资源时都会执行该方法中的逻辑，这表明本例中默认并未启用AuthorizationCache
   * @see 如果连续访问同一个URL（比如刷新），该方法不会被重复调用，Shiro有一个时间间隔<br/>
   * （也就是cache时间，在ehcache-shiro.xml中配置），超过这个时间间隔再刷新页面，该方法会被执行
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    logger.info("##################执行Shiro权限认证##################");
    //获取当前登录输入的用户名
    String username = (String) super.getAvailablePrincipal(principals);
    logger.info("##################开始查询用户【" + username + "】的权限##################");

    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    List<Resource> resources = resourceMapper.getPermissionByUsername(username);
    for (Resource resource : resources) {
      info.addStringPermission(resource.getPermisname());
      logger.info("##################权限【" + resource.getPermisname() + "】##################");
    }
    return info;
  }

  /**
   * 登陆验证
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
      throws AuthenticationException {
    //UsernamePasswordToken对象用来存放提交的登录信息
    UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
    logger.info("验证当前Subject时取到的token为：" + ReflectionToStringBuilder
        .toString(token, ToStringStyle.MULTI_LINE_STYLE));
    //查出是否有此用户
    String username = usernamePasswordToken.getUsername();
    Account account = accountMapper.selectAccountByUsername(username);
    if (account != null) {
      if (account.getUserstate().equals(2)) {
        throw new LockedAccountException("您的账户已被锁定");
      }
      // 若存在，将此用户存放到登录认证info中，无需自己做密码对比，SHIRO会为我们进行密码对比校验
      return new SimpleAuthenticationInfo(account.getUsername(),
          account.getUserpwd(), getName());
    }
    return null;
  }
}
